import React, {
  PropsWithChildren,
  CSSProperties,
  RefObject,
  ReactNode,
  useMemo,
  useState,
  useRef,
  useEffect,
} from "react";
import styled, { keyframes, css } from "styled-components";
import { color, typography } from "../shared/styles";
import { darken, rgba, opacify } from "polished";
import { easing, modalOpenAnimate, modalCloseAnimate } from "../shared/animation";
import { useStateAnimation } from "../../hooks/useStateAnimation"
import { Button } from "../button";
import { Icon } from "../icon";

// 判定点击组件外
export function useClickOutside(
  ref: RefObject<HTMLElement>,
  handler: Function
) {
  useEffect(() => {
    const listener = (event: MouseEvent) => {
      if (!ref.current || ref.current.contains(event.target as Node)) {
        return;
      }
      handler(event);
    };
    window.addEventListener("click", listener);
    return () => window.removeEventListener("click", listener);
  }, [ref, handler]);
}

const CalendarWrapper = styled.div<{ visible: boolean; delay: number }>`
  position: absolute;
  border: 1px solid black;
  transition: all ${(props) => props.delay / 1000}s cubic-bezier(0.23, 1, 0.32, 1);
  background: ${color.lightest};
  ${(props) =>
    props.visible &&
    css`
      animation: ${modalOpenAnimate} ${props.delay / 1000}s ease-in;
    `}
  ${(props) =>
    !props.visible &&
    css`
      animation: ${modalCloseAnimate} ${props.delay / 1000}s ease-in;
    `}
`;

const CalendarDateRow = styled.tr``;

const tableItemStyle = css`
  display: inline-block;
  min-width: 24px;
  height: 24px;
  line-height: 24px;
  border-radius: 2px;
  margin: 2px;
  text-align: center;
`;

const CalendarHeadItem = styled.td`
  ${tableItemStyle}
  cursor:default;
`;

const CalendarDate = styled.td<Partial<DateItem>>`
  display: inline-block;
  min-width: 24px;
  height: 24px;
  line-height: 24px;
  border-radius: 2px;
  margin: 2px;
  text-align: center;
  cursor: pointer;
    ${(props) => {
    if (props.isonDay) {
      //当天的 
      return `color:${color.lightest};background:${color.primary};`;
    }
    return `&:hover {color: ${color.secondary};};`;
  }}
  ${(props) => {
    if (props.isonMonth === false) {
      //不是当月显示灰色
      return `color:${color.mediumdark};`;
    }
    return "";
  }}
`;

const CalendarHeadWrapper = styled.div`
  padding: 10px;
  display: flex;
  color: ${rgba(0, 0, 0, 0.85)};
  border-bottom: 1px solid #f0f0f0;
  justify-content: center;
  flex-direction: column;
`;

type calDataType = [number, number, number];

const btnStyle = {
  padding: "0px",
  background: color.lightest,
};

const IconWrapper = styled.span`
  display: inline-block;
  vertical-align: middle;
  > svg {
    height: 12px;
  }
`;

const BtnDiv = styled.div`
  display: flex;
  jutify-content: center;
  align-items: center;
  height: 24px;
  line-height: 24px;
`;

const MonthWrapper = styled.div`
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  position: relative;
`;

const MonthItem = styled.div<{ toGrey?: boolean }>`
  width: 25%;
  height: 60px;
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
  ${(props) => props.toGrey && `color:${color.mediumdark};`}
  &:hover {
    color: ${color.secondary};
  }
`;

const Bwrapper = styled.b`
  cursor: pointer;
  &:hover {
    color: ${color.primary};
  }
`;

const CalendarIcon = styled.span`
  display: inline-block;
`;

const DatePickerWrapper = styled.div`
  display: inline-block;
  border-color: #40a9ff;
  border-right-width: 1px !important;
  outline: 0;
  box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2);
`;

interface DateItem {
  day: number; //天
  isonMonth: boolean; //当月
  isonDay: boolean; //当日
  origin: Date;
}

const isCurrentMonth = function (
  current: Date,
  year: number,
  month: number
): boolean {
  return current.getFullYear() === year && current.getMonth() === month;
};

const isCurrentDay = function (current: Date, day: number, onMonth: boolean) {
  return current.getDate() === day && onMonth;
};

const getDateData = function (year: number, month: number, day: number) {
  const firstDay = new Date(year, month, 1);
  let weekDay = firstDay.getDay(); //周日，0，周六 6
  weekDay = weekDay === 0 ? 7 : weekDay;
  let start = firstDay.getTime() - weekDay * 60 * 60 * 24 * 1000;
  let arr: DateItem[] = [];
  for (let i = 0; i < 42; i++) {
    let current = new Date(start + i * 60 * 60 * 24 * 1000);
    let onMonth = isCurrentMonth(current, year, month);
    arr.push({
      day: current.getDate(),
      isonMonth: onMonth,
      isonDay: isCurrentDay(current, day, onMonth),
      origin: current,
    });
  }
  let k = -1;
  return Array.from({ length: 6 }, () => {
    k++;
    return arr.slice(k * 7, (k + 1) * 7);
  });
};

const getYearMonthDay = function (date: number): calDataType {
  let tmp = new Date(date);
  return [tmp.getFullYear(), tmp.getMonth(), tmp.getDay()];
};

const getStartYear = function (calData: calDataType) {
  return calData[0] - calData[0] % 10
}

const generateDate = (calData: calDataType) => {
  return `${calData[0]}-${calData[1] + 1}-${calData[2]}`
}

const validateDate = (value: string) => {
  let reg = /^(\d{4})-(\d{1,2})-(\d{1,2})$/;
  if (reg.exec(value)) {
    return true;
  } else {
    return false;
  }
};

const MonthData = new Array(12).fill(1).map((_x, y) => y + 1);

type modeType = 'date' | 'month' | 'year'

export type DatepickerProps = {
  /** 日期选择的回调 */
  callback?: (v: string) => void;
  /**  动画速度 */
  delay?: number;
  /** 初始值*/
  initDate?: string;
  /** 外层样式*/
  style?: CSSProperties;
  /** 外层类名 */
  classname?: string;
};

export function DatePicker(props: DatepickerProps) {
  const { callback, initDate, delay = 200 } = props;
  const [mode, setMode] = useState<modeType>('date');

  const [show, setShow] = useState(false);

  const ref = useRef<HTMLDivElement>(null);

  const [st, setst, unmount] = useStateAnimation(setShow, 200);
  useClickOutside(ref, () => setst(false));

  const [calData, setCalData] = useState<calDataType>(() => [
    new Date().getFullYear(),
    new Date().getMonth(),
    new Date().getDate(),
  ]);

  const [state, setState] = useState(() => {
    if (initDate && validateDate(initDate)) {
      return initDate;
    } else {
      return generateDate(calData);
    }
  });

  useEffect(() => {
    if (callback) callback(state);
  }, [state, callback]);

  const dayData = useMemo(() => {
    const [year, month, day] = state.split('-');
    const arr = getDateData(Number(year), Number(month), Number(day)); //传的8实际是9
    return arr;
  }, [state]);

  const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setState(e.target.value);
  };

  const handleClick = () => {
    setShow(true);
  };

  const handleBlur = () => {
    if (state !== generateDate(calData)) {
      //如果相等，说明是calData赋值上去的
      let res = validateDate(state); //验证格式
      if (!res) {
        //错误用原来的
        setState(generateDate(calData));
      } else {
        //否则计算新值
        let p = state.split("-");
        let newDate = new Date(
          parseInt(p[0]),
          parseInt(p[1]) - 1,
          parseInt(p[2])
        );
        const newCal: calDataType = [
          newDate.getFullYear(),
          newDate.getMonth(),
          newDate.getDate(),
        ];
        setCalData(newCal);
        setState(generateDate(newCal));
      }
    }
  };

  const modeDay = (
    <table style={{ display: mode === "month" ? "none" : "table" }}>
      <thead>
        <tr>
          <CalendarHeadItem>日</CalendarHeadItem>
          <CalendarHeadItem>一</CalendarHeadItem>
          <CalendarHeadItem>二</CalendarHeadItem>
          <CalendarHeadItem>三</CalendarHeadItem>
          <CalendarHeadItem>四</CalendarHeadItem>
          <CalendarHeadItem>五</CalendarHeadItem>
          <CalendarHeadItem>六</CalendarHeadItem>
        </tr>
      </thead>
      <tbody>
        {dayData.map((v, index) => (
          <CalendarDateRow key={index}>
            {v.map((k, i) => (
              <CalendarDate
                isonDay={k.isonDay}
                isonMonth={k.isonMonth}
                key={i}
                onClick={() => {
                  const origin = k.origin;
                  const newCal: calDataType = [
                    origin.getFullYear(),
                    origin.getMonth() - 1,
                    origin.getDate(),
                  ];
                  setCalData(newCal);
                  setState(generateDate(newCal));
                  setst(false);
                }}
              >
                {k.day}
              </CalendarDate>
            ))}
          </CalendarDateRow>
        ))}
      </tbody>
    </table>
  );

  const modeMonth = (
    <MonthWrapper
      style={{ display: mode === "month" ? "flex" : "none" }}
    >
      {
        MonthData.map((v, i) => {
          return (
            <MonthItem
              key={i}
              onClick={() => {
                //获取当前月，与点击相减得差
                let diff = v - calData[1] - 1;
                let res = changeCalData(diff > 0, calData);
                setCalData(res);
                setMode("date");
              }}
            >
              {v}月
            </MonthItem>
          );
        })}
    </MonthWrapper>
  );

  const startYear = getStartYear(calData);
  const yearMap = new Array(12).fill(1).map((_x, y) => startYear + y - 1);

  const modeYear = (
    <MonthWrapper
      style={{ display: mode === "year" ? "flex" : "none" }}
    >
      {yearMap.map((v, i) => (
        <MonthItem
          toGrey={i === 0 || i === 11}
          key={i}
          onClick={() => {
            //获取选择的年与差值
            let diff = v - calData[0];
            let res = changeCalYear(diff, calData);
            setCalData(res);
            setMode("month");
          }}
        >
          {v}
        </MonthItem>
      ))}
    </MonthWrapper>
  );

  const changeCalData = function (
    sign: boolean,
    calData: calDataType
  ): calDataType {
    const oldDate = new Date(calData[0], calData[1]);
    if (sign) {
      //true是减少false是增加
      const newDate = oldDate.setMonth(oldDate.getMonth() - 1);
      return getYearMonthDay(newDate);
    } else {
      const newDate = oldDate.setMonth(oldDate.getMonth() + 1);
      return getYearMonthDay(newDate);
    }
  };

  const changeCalYear = function (sign: number, calData: calDataType) {
    const oldDate = new Date(calData[0], calData[1]);
    const newDate = oldDate.setFullYear(oldDate.getFullYear() + sign);
    return getYearMonthDay(newDate);
  };

  const render = useMemo(() => {
    const handleLeft = () => {
      let res: calDataType;
      if (mode === "date") {
        res = changeCalData(true, calData);
      } else if (mode === "month") {
        res = changeCalYear(-1, calData);
      } else {
        res = changeCalYear(-10, calData);
      }
      setCalData(res);
    };

    const handleRight = () => {
      let res: calDataType;
      if (mode === "date") {
        res = changeCalData(false, calData);
      } else if (mode === "month") {
        res = changeCalYear(1, calData);
      } else {
        res = changeCalYear(10, calData);
      }
      setCalData(res);
    };

    if (!show) {
      unmount();
      return null;
    } else {
      return (
        <CalendarWrapper visible={st} delay={delay}>
          <CalendarHeadWrapper>
            <div
              style={{
                display: "flex",
                justifyContent: "center",
                width: "240px",
              }}
            >
              <BtnDiv style={{ marginLeft: "20px" }}>
                <Button
                  size="small"
                  style={btnStyle}
                  onClick={() => handleLeft()}
                >
                  <IconWrapper>
                    <Icon icon="arrowleft"></Icon>
                  </IconWrapper>
                </Button>
              </BtnDiv>
              <BtnDiv style={{ flex: 1 }}>
                <span>
                  <Bwrapper
                    style={{
                      display:
                        mode === "year"
                          ? "inline-block"
                          : "none",
                    }}
                  >{`${startYear}-${startYear +
                    9}`}</Bwrapper>
                  <Bwrapper
                    onClick={() => {
                      setMode("year");
                    }}
                    style={{
                      display:
                        mode === "month" ||
                          mode === "date"
                          ? "inline-block"
                          : "none",
                    }}
                  >{`${calData[0]}年`}</Bwrapper>
                  <Bwrapper
                    onClick={() => {
                      setMode("month");
                    }}
                    style={{
                      display:
                        mode === "date"
                          ? "inline-block"
                          : "none",
                    }}
                  >{`${calData[1] + 1}月`}</Bwrapper>
                </span>
              </BtnDiv>
              <BtnDiv style={{ marginRight: "20px" }}>
                <Button
                  size="small"
                  style={btnStyle}
                  onClick={() => handleRight()}
                >
                  <IconWrapper>
                    <Icon icon="arrowright"></Icon>
                  </IconWrapper>
                </Button>
              </BtnDiv>
            </div>

            <div
              style={{
                // width: "240px",
                display: "flex",
                justifyContent: "center",
              }}
            >
              {modeDay}
              {modeMonth}
              {modeYear}
            </div>
          </CalendarHeadWrapper>
        </CalendarWrapper>
      );
    }
  }, [show, unmount, st, calData, dayData, setst, mode]);

  return (
    <DatePickerWrapper ref={ref} onClick={handleClick}>
      <input
        aria-label="date picker"
        onBlur={handleBlur}
        value={state}
        onChange={handleChange}
        style={{ border: "none", boxShadow: "none", outline: "none" }}
      ></input>
      <CalendarIcon>
        <Icon icon="calendar"></Icon>
      </CalendarIcon>
      {render}
    </DatePickerWrapper>
  );
}

DatePicker.defaultProps = {
  delay: 200,
};